package org.arl.modem.codec;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.Random;

public class LTEncoder extends Encoder {
   // protected int seed;
   // protected boolean[] position;
   // protected byte[][] message;
   // protected int galoisField;
   // public int sizeOfPacket;
   // public int numOfMessages;
   // protected int numOfPackets;
   // protected int numOfSourceMessages;
   protected float probOfFailure;

   // protected float constantC;
   // private fileName;

   /*
    * Decription : Constructor for LT encoder using uniform random number
    * generator Class : LtRlEncoder Input arguments: fieldSize : size of the
    * Galois Field (currently it supports only a field size of 2) pktSize : size
    * of each message symbol numOfPackets : number of packets that can be sent
    * to the channel (currently it stores values and does not queue packets)
    * probOfFailure: Probability that the decoder is unable to decode the packet
    * when k encoded symbols are received. This is the δ parameter in the RS
    * distribution constantC: This is the constant c in RS distribution, which
    * is greater than 0. Returns : None
    */
   public LTEncoder(int fieldSize, int pktSize, int numOfPackets,
         float probOfFailure, float constantC, byte[] b) {

      galoisField = fieldSize;
      sizeOfPacket = pktSize;
      packet = new byte[sizeOfPacket];
      this.numOfPackets = numOfPackets;
      this.probOfFailure = probOfFailure;
      this.constantC = constantC;
      this.bytes = b;
   }

   /*
    * Description: Reads bytes from the input byte stream Input arguments: none
    * Returns: 1 on successful read
    */
   public int readMessage() {
      float numOfSourceMessages = 0;
      numOfMessages = bytes.length;
      numOfSourceMessages = (float) Math.ceil((float) numOfMessages
            / sizeOfPacket);
      System.out.println("Number of Source Messages generated"
            + numOfSourceMessages);

      this.numOfSourceMessages = (int) numOfSourceMessages;
      System.out.println("number of bytes in the file " + numOfMessages);
      message = new byte[this.numOfSourceMessages][sizeOfPacket];

      for (int i = 0; i < this.numOfSourceMessages; i++) {
         for (int k = 0; k < sizeOfPacket; k++) {
            if ((i * sizeOfPacket + k) < bytes.length)
               message[i][k] = bytes[i * sizeOfPacket + k];
         }
      }

      return numOfMessages;
   }

   /*
    * Description: Encodes message symbols to encoded symbols Input arguments:
    * none Returns : encoded packet
    */
   public CodecPacket encodeMessage() {

      int randomNumber = 0;
      degree = 0;
      int index = 0;
      byte[] temp = new byte[sizeOfPacket];

      int index1 = 0;
      int index2 = 0;
      boolean flag = false;
      Random randGen = null;
      randGen = new Random();
      RobustSoliton obj = new RobustSoliton();
      degree = obj.randomnumgen(numOfSourceMessages, (float) probOfFailure,
            (float) constantC);
      // degree++;
      // System.out.println("degree is "+degree);
      // degree=randGen.nextInt(numOfSourceMessages+1);
      // System.out.println("degree is "+degree);
      int[] checkRandom = new int[degree];
      Arrays.fill(checkRandom, -1);
      Arrays.fill(temp, (byte) 0);
      randGen = new Random();
      seed = randGen.nextInt(numOfSourceMessages + 1);

      randomNumber = seed;

      randGen = new Random(randomNumber);
      for (index = 0; index < degree; index++) {
         randomNumber = randGen.nextInt(numOfSourceMessages);
         // System.out.println("Location " + randomNumber);
         checkRandom[index] = randomNumber;
         flag = false;

         for (index1 = 0; index1 < index; index1++) {
            if (randomNumber == checkRandom[index1]) {
               index = index - 1;
               flag = true;
               break;
            }
         }
         if (flag == true) {
            continue;
         }
         // System.out.println(randomNumber);
         for (index2 = 0; index2 < sizeOfPacket; index2++) {
            temp[index2] = (byte) (temp[index2] ^ message[randomNumber][index2]);
         }

      }

      for (index2 = 0; index2 < sizeOfPacket; index2++) {
         packet[index2] = temp[index2];
      }
      CodecPacket cp = new CodecPacket(packet, seed, degree);
      return cp;

   }

   /*
    * Description: writes encoded symbol to the channel stream Input arguments:
    * packet - encoded packet Returns: None
    */
   public void writePacket(byte[] packet) {

      FileOutputStream outFile = null;
      ObjectOutputStream out = null;
      int index = 0;
      try {
         outFile = new FileOutputStream("channel.dat");
         out = new ObjectOutputStream(outFile);
         CodecPacket obj = new CodecPacket();
         obj.header = seed;
         obj.packet = new byte[sizeOfPacket];
         for (index = 0; index < sizeOfPacket; index++) {

            obj.packet[index] = packet[index];
         }
         obj.degree = degree;

         out.writeObject(obj);
      } catch (IOException ioe) {
      } finally {
         if (out != null) {
            try {
               out.close();
            } catch (IOException ioe) {
            }
         }
      }
   }

   @Override
   public boolean packetAvailable() {
      return false;
   }

   @Override
   public void readPacket() {

   }

   @Override
   public int getCodedPacketSize() {
      return -1;
   }

   @Override
   public int numPacketsReceived() {
      return 0;
   }

   @Override
   public void writeMessage() {
      throw new UnsupportedOperationException("Not supported yet.");
   }

   @Override
   public void decodeMessage(CodecPacket packet) {
      
   }

}
